/*

 ============================================================================
 Name        : lightmanager.c
 Author      : zwiebelchen
 Version     :
 Copyright   : GPL
 Description : main file which creates server socket and sends commands to
 LightManager pro via USB
 ============================================================================
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <libusb-1.0/libusb.h>

void sendToUSB(libusb_device_handle* dev_handle, unsigned char* deviceCode) {
	int actual;
	libusb_interrupt_transfer(dev_handle, (1 | LIBUSB_ENDPOINT_OUT), deviceCode, 8, &actual, 500);
	libusb_interrupt_transfer(dev_handle, (0x82 | LIBUSB_ENDPOINT_IN), deviceCode, 8, &actual, 500);
}

int handleInput(char* input, libusb_device_handle* dev_handle) {
	// FS20 devices, "FS20_1_13_ON" or "FS20_16_4_TOGGLE" or "FS20_4_3_TOGGLE"
	if (strncmp(input, "FS20_", 5) == 0) {
		char *cp;
		int state;
		int group = (strtol(input + 5, &cp, 10)-1) * 0x10;
		int subgroup = strtol(cp+1, &cp, 10) - 1;
		if (strncmp(cp, "_ON", 3) == 0) {
			state = 0x11;
		} else if (strncmp(cp, "_OFF", 4) == 0) {
			state = 0x00;
		} else if (strncmp(cp, "_TOGGLE", 7) == 0) {
			state = 0x12;
		} else if (strncmp(cp, "_UP", 3) == 0) {
			state = 0x13;
		} else if (strncmp(cp, "_DOWN", 5) == 0) {
			state = 0x14;
		}

		// dimming case
		else if (strncmp(cp, "_", 1) == 0) {
			errno = 0;
			cp++;
			int dimValue = strtol(cp, NULL, 10);
			if (errno != 0 || dimValue < 0 || dimValue > 16) {
				state = -1;
				printf("Wrong dim level, valid is 0 to 16 !\n");
			}
			else {
				state = 0x01 * dimValue;
			}
		} else {
			state = -1;
		}
		if (state >= 0) {
			// housecode 0, needs to be implemented
			static char cmd[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00 };
			cmd[3] = group + subgroup;
			cmd[4] = state;

			sendToUSB(dev_handle, (unsigned char *) cmd);
		}
	}
	// Scenes, starting with "SCENE_1" up to "SCENE_65"
	else if (strncmp(input, "SCENE_", 6) == 0 && input[6] > '0') {
		static char cmd[8] = { 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
		cmd[1] = 0x01 * strtol(input + 6, NULL, 10);

		sendToUSB(dev_handle, (unsigned char *) cmd);
	}
	// InterTechno devices, "A3_ON" or "B6_OFF" or "H3_TOGGLE"
	else if ((input[0] >= 'A' && input[0] <= 'P')
			&& (input[1] >= '0' && input[1] <= '9')) {
		char *cp;
		int num = strtol(input + 1, &cp, 10);
		int state;
		int port;
		if (strncmp(cp, "_ON", 3) == 0) {
			state = 1;
		} else if (strncmp(cp, "_OFF", 4) == 0) {
			state = 0;
		} else if (strncmp(cp, "_TOGGLE", 7) == 0) {
			state = 2;
		} else {
			state = -1;
		}
		if (num >= 1 && num <= 16 && state >= 0) {
			static char cmd[8] = { 0x05, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
			port = (input[0] - 'A') * 0x10 + (num - 1);
			cmd[1] = port;
			cmd[2] = state;

			sendToUSB(dev_handle, (unsigned char *) cmd);
		}

	}
	return 0; //success
}

int main(int argc, char * argv[]) {
	if (argc == 1) {
		printf("usage: lightmanagerc C3_OFF\n");
		return 0;
	}

	libusb_device_handle *dev_handle;
	libusb_context *usbContext = NULL;
	int r; //for return values
	r = libusb_init(&usbContext);
	if (r < 0) {
		printf("Init Error %i\n", r);
		return 1;
	}

	dev_handle = libusb_open_device_with_vid_pid(usbContext, 5824, 2610); /*VendorID and ProductID in decimal*/
	if (dev_handle == NULL ) {
		printf("Cannot open device\n");
	}
	if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
		if (libusb_detach_kernel_driver(dev_handle, 0) == 0) {
		} else {
			printf("Kernel Driver not Detached!\n");
		}
	}
	r = libusb_claim_interface(dev_handle, 0);
	if (r < 0) {
		printf("Cannot Claim Interface\n");
		return 1;
	}

	int i = 0;

	//translating everything to uppercase
	for (i = 1; i < argc; i++) {
		int n = 0;
		for (n = 0; argv[i][n] != '\0'; n++) {
			if (argv[i][n] == '-') {
				argv[i][n] = '_';
			}
			argv[i][n] = toupper(argv[i][n]);
		}
		handleInput(argv[i], dev_handle);
	}

	r = libusb_release_interface(dev_handle, 0);
	if (r != 0) {
		printf("Cannot Release Interface\n");
		return 1;
	}

	libusb_close(dev_handle);
	libusb_exit(usbContext);
	return 0;
}
